package com.wh1200.mybatis.xmlless.dialect

import com.wh1200.mybatis.xmlless.constant.*
import com.wh1200.mybatis.xmlless.model.FieldMapping
import com.wh1200.mybatis.xmlless.util.ColumnUtils
import net.sf.jsqlparser.expression.Function
import net.sf.jsqlparser.expression.StringValue

/**
 * MySQL方言
 *
 * @author 吴昊
 * @date 2023/12/18
 * @version 1.0
 * @since v4.0.0
 */
object MySqlDialect : IDialect {

  override fun toLimitSql(
      isSpringDataPageable: Boolean,
      pageableParamName: String?,
      offsetParam: String,
      sizeParam: String
  ): String {
    if (isSpringDataPageable) {
      return SPRING_PAGEABLE_LIMIT.format(
          pageableParamName, offsetParam, sizeParam
      )
    }
    return LIMIT.format(offsetParam, sizeParam)
  }

  override fun wrapColumnIfMatchKeywords(column: String): String {
    return "`$column`"
  }

  override fun createJsonArrayElementEqual(columnResult: String, param: String): String {
    return """JSON_CONTAINS(%s, ${'$'}{"'" + ${Strings.jsonCall("%s")} + "'"}, '$')""".format(
        columnResult, param
    )
  }

  override fun toJsonPath(parentMapping: FieldMapping, relativePath: String): String {
    return parentMapping.column + "->'" + if (parentMapping.isJsonArray) {
      "$[*].$relativePath"
    } else {
      "$.$relativePath"
    } + "'"
  }

  override fun createJsonArrayElementIn(columnResult: String, paramOrValue: String, value: Boolean): String {
    if (value) {
      return paramOrValue
    }
    return """
      <foreach collection="$paramOrValue" item="item" separator=" or ">
        ${createJsonArrayElementEqual(columnResult, "item")}
      </foreach>
    """.trimIndent()
  }

  override fun getInsertOrUpdateTemplate(): String {
    return INSERT_OR_UPDATE
  }

  override fun getBatchInsertOrUpdateTemplate(): String {
    return BATCH_INSERT_OR_UPDATE
  }

  override fun createUpsertFieldSetExpression(column: String): String {
    val wrapColumn = ColumnUtils.wrapColumn(column)
    return "(if(VALUES($wrapColumn) = null, $wrapColumn, VALUES($wrapColumn)))"
  }

  override fun createJoinJsonArrayElementEqual(column: String, value: String): String {
    return "JSON_CONTAINS(${column}, CAST(${value} AS JSON), '$')"
  }

  override fun createJsonRemoveExpression(column: String, parameterName: String, isNumberType: Boolean): String {
    if (isNumberType) {
      return """JSON_REMOVE(
          $column, JSON_UNQUOTE(JSON_SEARCH(CAST(REPLACE(
          REPLACE(REPLACE(REPLACE($column, ' ', ''), '[', '["'), ']', '"]'),
        ',', '","') AS JSON), 'one', CONCAT('', #{${parameterName}}, ''))))""".trimIndent()
    }
    return """JSON_REMOVE(
          $column, JSON_UNQUOTE(JSON_SEARCH($column, 'one', CONCAT('', #{${parameterName}}, ''))))""".trimIndent()
  }

  override fun createJsonAddExpression(column: String, parameterName: String): String {
    val valueExp = """ CAST(${'$'}{"'" + ${Strings.jsonCall(parameterName)} + "'"} AS JSON)"""
    return "JSON_ARRAY_APPEND($column, '\$', $valueExp)"
  }

  override fun replaceDateFunction(function: Function) {
    val functionName = function.name.lowercase()
    function.name = "DATE_FORMAT"
    when (functionName) {
      "year"  -> {
        function.parameters.addExpressions(StringValue("%Y"))
      }

      "date"  -> {
        function.parameters.addExpressions(StringValue("%Y-%m-%d"))
      }

      "month" -> {
        function.parameters.addExpressions(StringValue("%Y-%m"))
      }

      else    -> error("")
    }
  }

}
